﻿// Desktop manager class, (c)2010 Knihovnik
// This file is licensed under GNU GPL license v2, see more at http://www.gnu.org/licenses/gpl-2.0.html
namespace vKapse.VirtualDesktop.Core
{

	#region Using

	using System;
	using System.Collections.Generic;
	using System.Diagnostics;
	using System.Drawing;
	using System.IO;
	using System.Text;
	using System.Windows.Forms;
	using System.Xml;
	using System.Xml.Serialization;
	using vKapse.VirtualDesktop.Core.UI;

	#endregion Using

	/// <summary>
	/// Class represents manager of desktops
	/// </summary>
	public class DesktopManager
	{

		#region Members

		private List<Desktop> m_desktops; // All desktops
		private List<SharedProcess> m_shared; // All shared processes
		private Desktop m_active; // Currently active desktop
		private NotifyIcon m_notifyIcon; // Notify icon (in system tray)
		private MenuItem m_prevMi; // Previous clicked context menu item
		private bool m_shareFolders; // Share explorer folders?
		private DesktopSwitcherForm m_desktopsForm;

		private const string CONFIG_FILE = "vDesktop.config";
		private const string SHARE_FILE = "Share.config";
		private const string CONFIG_DIR = "vDesktop";

		private static volatile DesktopManager _instance = null;
		private static object _syncRoot = new object();

		#endregion Members

		#region Constructor

		/// <summary>
		/// Constructor
		/// </summary>
		public DesktopManager()
		{
			if (_instance != null) // To prevent to make second instance of DesktopManager..
			{
				throw new ApplicationException();
			}

			this.m_desktops = new List<Desktop>();
			this.m_shared = new List<SharedProcess>();

			Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
			_instance = this;
		}

		#endregion Constructor

		#region Methods
		
		/// <summary>
		/// Method checks for if process <paramref name="processName"/> is shared between desktops
		/// </summary>
		/// <param name="processName">Process name</param>
		/// <returns>If it's shared, returns <strong>True</strong>.</returns>
		internal bool ContainsShared(string processName)
		{
			return this.m_shared.Contains(new SharedProcess(processName));
		}

		/// <summary>
		/// Method saves all desktops to configuration file
		/// </summary>
		/// <param name="desktops">Array of desktops</param>
		private static void SaveDesktops(Desktop[] desktops)
		{
			string dirPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
				string.Format(@"{0}\", DesktopManager.CONFIG_DIR));
			if(!Directory.Exists(dirPath))
			{
				Directory.CreateDirectory(dirPath);
			}

			string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), 
				string.Format(@"{0}\{1}", DesktopManager.CONFIG_DIR, DesktopManager.CONFIG_FILE));
			using (XmlTextWriter writer = new XmlTextWriter(path, Encoding.UTF8))
			{
				XmlSerializer serializer = new XmlSerializer(typeof(Desktop[]));
				serializer.Serialize(writer, desktops);
			}

		}

		/// <summary>
		/// Method loads desktops from config
		/// </summary>
		/// <returns>Array of loaded desktops</returns>
		private static Desktop[] LoadDesktops()
		{
			string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), 
				string.Format(@"{0}\{1}", DesktopManager.CONFIG_DIR, DesktopManager.CONFIG_FILE));
			if (!File.Exists(path))
			{
				return null;
			}
			Desktop[] desktops = null;
			using (XmlTextReader reader = new XmlTextReader(path))
			{
				XmlSerializer serializer = new XmlSerializer(typeof(Desktop[]));
				desktops= (Desktop[])serializer.Deserialize(reader);
			}
			return desktops;
		}

		/// <summary>
		/// Initialize desktop manager
		/// </summary>
		/// <returns><see cref="NotifyIcon"/></returns>
		public NotifyIcon Init()
		{
			// We try to load desktops from file
			Desktop[] loaded = DesktopManager.LoadDesktops();
			if (loaded == null || loaded.Length == 0) // If there is NO config file
			{
				// Default desktops ;)
				this.m_desktops.Add(new Desktop("Work"));
				this.m_desktops.Add(new Desktop("Internet"));
				this.m_desktops.Add(new Desktop("Media"));
				this.m_desktops.Add(new Desktop("Stuff"));
			}
			else
			{
				this.m_desktops.AddRange(loaded);
			}

			// Create context menu in systray
			NotifyIcon nicon = new NotifyIcon();
			ContextMenu cm = new ContextMenu();
			
			foreach (Desktop desktop in this.m_desktops)
			{
				desktop.Activated += new EventHandler(this.DesktopActivated);

				MenuItem mi = new MenuItem(desktop.LongName);
				mi.Tag = desktop;
				mi.Click += new EventHandler(this.MenuItem_Click);
				mi.ShowShortcut = true;
				cm.MenuItems.Add(mi);

				desktop.Tag = mi;
				desktop.PropertiesChanged += new EventHandler(desktop_PropertiesChanged);
			}
			
			cm.MenuItems.Add(new MenuItem("-"));

			MenuItem folderShareMenuItem = new MenuItem("Folder share");
			folderShareMenuItem.Click += new EventHandler(folderShareMenuItem_Click);
			// cm.MenuItems.Add(folderShareMenuItem);
			// TODO: Fix this later..

			MenuItem shareMenuItem = new MenuItem("Share");
			MenuItem addShareMenuItem = new MenuItem("Add");
			addShareMenuItem.Click += new EventHandler(addShareMenuItem_Click);
			shareMenuItem.MenuItems.Add(addShareMenuItem);
			shareMenuItem.MenuItems.Add(new MenuItem("-"));
			
			cm.MenuItems.Add(shareMenuItem);
			
			cm.MenuItems.Add(new MenuItem("-"));
			MenuItem aboutMenuItem = new MenuItem("About");
			aboutMenuItem.Click += new EventHandler(aboutMenuItem_Click);
			cm.MenuItems.Add(aboutMenuItem);
			cm.MenuItems.Add(new MenuItem("-"));
			MenuItem exitMenuItem = new MenuItem("Exit");
			exitMenuItem.Click += new EventHandler(this.ExitMenuItem_Click);
			cm.MenuItems.Add(exitMenuItem);


			nicon.ContextMenu = cm;
			nicon.Visible = true;
			nicon.Icon = SystemIcons.Application;

			this.m_notifyIcon = nicon;

			this.m_desktopsForm = new DesktopSwitcherForm();
			return nicon;
		}

		void aboutMenuItem_Click(object sender, EventArgs e)
		{
			StringBuilder sb = new StringBuilder();
			sb.AppendFormat("vDesktop v.{0}", Application.ProductVersion);
			sb.AppendLine("(c)2008-2010 David Schwarz");
			sb.Append("http://www.google.com/profiles/vkapse.davecz#about");
			
			MessageBox.Show(sb.ToString(), "About", MessageBoxButtons.OK, MessageBoxIcon.Information);
		}

		/// <summary>
		/// Methods exits virtual desktop application (and shows all applications in all desktops)
		/// </summary>
		private void Exit()
		{
			DesktopManager.SaveDesktops(this.m_desktops.ToArray());
			this.m_desktops.ForEach(delegate(Desktop desktop) { desktop.Show(); });
			this.m_notifyIcon.Visible = false;
			HotKeyWrapper.UnregisterAll();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="desktop"></param>
		public void Show(Desktop desktop)
		{
			if (this.m_active != null)
			{
				this.m_active.Update();
				if (desktop != this.m_active)
				{
					this.m_active.Hide();
				}
			}
			else
			{
				// Active is null, update this one then
				this.m_active = desktop;
				desktop.Update();
				this.m_active = null;
			}
			desktop.Show();
			this.m_active = desktop;
		}

		#endregion Methods

		#region Properties

		/// <summary>
		/// 
		/// </summary>
		public bool ShareFolders
		{
			get
			{
				return this.m_shareFolders;
			}
			set
			{
				if (value == this.m_shareFolders)
				{
					return;
				}
				this.m_shareFolders = value;
				Properties.Settings.Default.FolderShare = value;
				if (value)
				{
					// Blah
				}
			}
		}

		/// <summary>
		/// Returns instance of <see cref="DesktopManager"/>
		/// </summary>
		internal static DesktopManager Instance
		{
			get
			{
				return _instance;
			}
		}

		/// <summary>
		/// Gets list of desktops
		/// </summary>
		public Desktop[] Desktops
		{
			get
			{
				return this.m_desktops.ToArray();
			}
		}

		/// <summary>
		/// Gets instance of active desktop
		/// </summary>
		public Desktop ActiveDesktop
		{
			get
			{
				return this.m_active;
			}
		}

		#endregion Properties

		#region Event Reactions

		void addShareMenuItem_Click(object sender, EventArgs e)
		{
			using (AddShareForm addShareForm = new AddShareForm())
			{
				if (addShareForm.ShowDialog() == DialogResult.OK)
				{
					string processName = addShareForm.ProcessName;
					if (processName.Length > 0)
					{
						this.AddShare(((MenuItem)sender), processName);
					}
				}
			}
		}

		private void AddShare(MenuItem sender, string processName)
		{
			MenuItem miProcessShare = new MenuItem(processName);
			miProcessShare.Click += new EventHandler(miProcessShare_Click);
			Menu parent = sender.Parent;
			parent.MenuItems.Add(miProcessShare);

			SharedProcess shared = new SharedProcess(processName);
			this.m_shared.Add(shared);

			foreach (Desktop desktop in this.m_desktops)
			{
				desktop.ReleaseProcess(processName);
			}
		}

		void miProcessShare_Click(object sender, EventArgs e)
		{
			MenuItem sharedProcessMenuItem = (MenuItem)sender;
			DialogResult result = MessageBox.Show(string.Format("Do you want to remove shared process \"{0}\" ?", 
				sharedProcessMenuItem.Text), "Remove", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
			if (result == DialogResult.Yes)
			{
				Menu parent = sharedProcessMenuItem.Parent;
				sharedProcessMenuItem.Click -= new EventHandler(miProcessShare_Click);
				parent.MenuItems.Remove(sharedProcessMenuItem);

				this.m_shared.Remove(new SharedProcess(sharedProcessMenuItem.Text));
			}
		}

		void folderShareMenuItem_Click(object sender, EventArgs e)
		{
			this.ShareFolders = !this.ShareFolders;
			((MenuItem)(sender)).Checked = this.ShareFolders;
		}

		void DesktopActivated(object sender, EventArgs e)
		{
			Desktop desktop = (Desktop)sender;
			if (this.ActiveDesktop == desktop)
			{
				return;
			}

			MenuItem menuItem = (MenuItem)desktop.Tag;
			menuItem.Checked = true;
			if (this.m_prevMi != null)
			{
				this.m_prevMi.Checked = false;
			}
			this.m_prevMi = menuItem;
		}

		[DebuggerStepThrough]
		void ExitMenuItem_Click(object sender, EventArgs e)
		{
			Application.Exit();
		}

		[DebuggerStepThrough]
		void MenuItem_Click(object sender, EventArgs e)
		{
			MenuItem mi = ((MenuItem)sender);
			this.Show((Desktop)mi.Tag);
		}

		//[DebuggerStepThrough]
		void desktop_PropertiesChanged(object sender, EventArgs e)
		{
			Desktop desktop = (Desktop)sender;
			MenuItem menuItem = ((MenuItem)desktop.Tag);
			menuItem.Text = desktop.LongName;
			
			//menuItem.MenuItems.Clear();
			//StringBuilder sb = new StringBuilder();
			//foreach (IntPtr ptr in desktop.Windows.Keys)
			//{
				//DesktopWindow window = desktop.Windows[ptr];
				//sb.Append(window.Name);
				//sb.Append(Environment.NewLine);
				//MenuItem windowItem = new MenuItem(window.Name);
				//menuItem.MenuItems.Add(windowItem);
			//}
			
		}

		[DebuggerStepThrough]
		void Application_ApplicationExit(object sender, EventArgs e)
		{
			this.Exit();
		}

		#endregion Event Reactions

	}
}
